package com.solo.eggsolo.util;

import java.util.Base64;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
/**
 * 
 * @author Yu
 *
 */
public class SymmetryDesAes {
	private final static String DES = "DES";

	private final static String AES = "AES";

	/**
	 * 设置为CBC加密模式，默认情况下ECB比CBC更高效
	 */
	private final static String CBC = "/CBC/PKCS5Padding";

	public static void main(String[] args) {
		// 对称加密 密文
		String input = "key=85CD019515D14B91AD942787532314FF&startTime=1629431243245&endTime=1660967243244";
		//input = "0";
		// DES加密算法，key的大小必须是8个字节
		String desKey = "12345678";

		String encryptDes = encryptBySymmetry(input, desKey, SymmetryDesAes.DES, true);
		System.out.println("DES加密后密文:" + encryptDes);

		//desKey = "87654321"; 密码错误会解密失败
		String des = decryptBySymmetry(encryptDes, desKey, SymmetryDesAes.DES, true);
		System.out.println("DES解密后明文:" + des);
		System.out.println("\n==============================================\n");

		
		
		// AES加密算法，key的大小必须是16个字节
		String aesKey = "1234567812345678";
		String encryptAes = encryptBySymmetry(input, aesKey, SymmetryDesAes.AES);
		System.out.println("AES加密后密文:" + encryptAes);
		
		//aesKey = "1234567887654321"; 密码错误会解密失败
		String aes = decryptBySymmetry(encryptAes, aesKey, SymmetryDesAes.AES);
		System.out.println("AES解密后明文:" + aes);
	}

	/**
	 * 对称加密
	 *
	 * @param input     : 密文
	 * @param key       : 密钥
	 * @param algorithm : 类型：DES、AES
	 * @return
	 */
	public static String encryptBySymmetry(String input, String key, String algorithm) {
		return encryptBySymmetry(input, key, algorithm, false);
	}

	/**
	 * 对称解密
	 *
	 * @param input     : 密文
	 * @param key       : 密钥
	 * @param algorithm : 类型：DES、AES
	 * @return
	 */
	public static String decryptBySymmetry(String input, String key, String algorithm) {
		return decryptBySymmetry(input, key, algorithm, false);
	}

	/**
	 * 对称加密数据
	 *
	 * @param input     : 原文
	 * @param key       : 密钥
	 * @param algorithm : 类型：DES、AES
	 * @param cbc       : CBC加密模式：同样的原文生成的密文不一样,串行进行，加密使用CBC解密也需要CBC
	 * @return : 密文
	 * @throws Exception
	 */
	public static String encryptBySymmetry(String input, String key, String algorithm, Boolean cbc) {
		try {
			// 根据加密类型判断key字节数
			checkAlgorithmAndKey(key, algorithm);

			// CBC模式
			String transformation = cbc ? algorithm + CBC : algorithm;
			// 获取加密对象
			Cipher cipher = Cipher.getInstance(transformation);
			// 创建加密规则
			// 第一个参数key的字节
			// 第二个参数表示加密算法
			SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);

			// ENCRYPT_MODE：加密模式
			// DECRYPT_MODE: 解密模式
			// 初始化加密模式和算法
			// 默认采用ECB加密：同样的原文生成同样的密文,并行进行
			// CBC加密：同样的原文生成的密文不一样,串行进行
			if (cbc) {
				// 使用CBC模式
				IvParameterSpec iv = new IvParameterSpec(key.getBytes());
				cipher.init(Cipher.ENCRYPT_MODE, sks, iv);
			} else {
				cipher.init(Cipher.ENCRYPT_MODE, sks);
			}

			// 加密
			byte[] bytes = cipher.doFinal(input.getBytes());

			// 输出加密后的数据
			return Base64.getEncoder().encodeToString(bytes);
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException("加密失败！");
		}
	}

	/**
	 * 对称解密
	 *
	 * @param input     : 密文
	 * @param key       : 密钥
	 * @param algorithm : 类型：DES、AES
	 * @param cbc       : CBC加密模式：同样的原文生成的密文不一样,串行进行，加密使用CBC解密也需要CBC
	 * @throws Exception
	 * @return: 原文
	 */
	public static String decryptBySymmetry(String input, String key, String algorithm, Boolean cbc) {
		try {
			// 根据加密类型判断key字节数
			checkAlgorithmAndKey(key, algorithm);

			// CBC模式
			String transformation = cbc ? algorithm + CBC : algorithm;

			// 1,获取Cipher对象
			Cipher cipher = Cipher.getInstance(transformation);
			// 指定密钥规则
			SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
			// 默认采用ECB加密：同样的原文生成同样的密文
			// CBC加密：同样的原文生成的密文不一样
			if (cbc) {
				// 使用CBC模式
				IvParameterSpec iv = new IvParameterSpec(key.getBytes());
				cipher.init(Cipher.DECRYPT_MODE, sks, iv);
			} else {
				cipher.init(Cipher.DECRYPT_MODE, sks);
			}
			// 3. 解密，上面使用的base64编码，下面直接用密文
			byte[] bytes = cipher.doFinal(Base64.getDecoder().decode(input));
			// 因为是明文，所以直接返回
			return new String(bytes);
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException("解密失败！");
		}
	}

	private static void checkAlgorithmAndKey(String key, String algorithm) {
		// 根据加密类型判断key字节数
		int length = key.getBytes().length;
		boolean typeEnable = false;
		if (DES.equals(algorithm)) {
			typeEnable = length == 8;
		} else if (AES.equals(algorithm)) {
			typeEnable = length == 16;
		} else {
			throw new RuntimeException("加密类型不存在");
		}
		if (!typeEnable) {
			throw new RuntimeException("加密Key错误");
		}
	}
}
